import scala.collection.mutable.HashMap
import scala.collection.immutable.List
import scala.collection.mutable.HashSet

package dataflowVariety
{
 
  object AttributeFactory 
  {
	  //for all custom attributes 
	  var CA = HashMap[String,(CustomAttribute,HashMap[Int,List[Equation]])]()
	  
	  //to know max order of equations found 
	  var maxOrder = 0
	  
	  //for equations arrangement
	  var equations = HashMap[Int,List[Equation]]();
	  
	  //for equations of first order (assignments)
	  //var assigns = HashSet[String]()             (DEPRECATED)
	  
	  def getVirtualFromTerm(eq:List[(String,Double)]):CustomAttribute = 
	  {
	    //for return value
	    var a:CustomAttribute = null	    
	    
	    var eqOrder = eq.size -1 //minus constant
	    
	    var e:Equation = null
	    
	    //insert to equation map
	    if (eqOrder > 0) 
	    {
	    	if (! equations.contains(eqOrder)) equations(eqOrder) = List[Equation]()
	    	e = new Equation(eq)
	    	equations(eqOrder) :+= e 
	    	if (eqOrder > maxOrder) maxOrder = eqOrder 
	    }
	    
	    //iterating over equation vars
	    for (t <- eq)
	    {
	      if(t._1 != "$")
	      {
	    	var tempCa:CustomAttribute = null
	    	
	    	if (CA.contains(t._1))
	    	{
	    	  tempCa = CA(t._1)._1	    		
	    	}
	    	else
	    	{
	    	  //creating new CA for table
	    	  tempCa = new CustomAttribute(t._1);
	    	  
	    	  //if this is the virtual one, we should return it
	    	  if (t._1.contains("virtual"))
	    	  {
	    	    a = tempCa
	    	  }	
	    	  
	    	  CA(t._1) = (tempCa,new HashMap[Int,List[Equation]]())
	    	}
	    	
	    	if ( ! CA(t._1)._2.contains(eqOrder))
	    	{
	    	  CA(t._1)._2(eqOrder) = List[Equation]()
	    	}
	    	
	    	CA(t._1)._2(eqOrder):+ e
	    	
	    	//if (eqOrder == 1) assigns += t._1 
	      }
	    }
	    
	    //return wanted virtual custom attribute 
	    a;
	  }
	  
	  def loadDataFromTerm(eq:List[(String,Double)])
	  {
	    getVirtualFromTerm(eq);	    
	  }
	  
	  def changeCustomAttr(name:String,value:String)
	  {
	    var changed = List[String]()
	    var solved = HashSet[Equation]()
	    
	    //initializing variable
	    if (CA.contains(name))
	    {
	      CA(name)._1.setVal(value)
	      changed :+= name
	    }
	    
	    //staring with lowest order equation
	    var currentOrder = 1
	    
	    if (equations.contains(currentOrder))
	    {
	    	var l = equations(currentOrder)      
	      
	        //for each equation the attribute apears 
	        for(e <- l)
	        {
	        	//adding to changed custom attributes
	        	changed :+= e.solve(changed,CA)
	        } 	           
	    }
	    
	    //going to next order equations
	    currentOrder += 1
	    
	    while (currentOrder <= maxOrder) 
	    {       
	      if (equations.contains(currentOrder))
	      {
	        var solving = true

	        while (solving)
	        {
	         //until we will solve another equation
	         solving = false
	         
	         for (v<-changed) 
	         {
	           if (CA(v)._2.contains(currentOrder))
	           {
		           for (e<-CA(v)._2(currentOrder))
		           {
				         if ( ! solved.contains(e))
				         {
				           if (e.solvable(changed))	          
				           {            
				            changed:+=e.solve(changed,CA)
				            solved += e
				            
				            //we need to continue solving now
				            solving = true
				           }	          
				         }
			       }
	           }
		         
	         }
	        
	        }
	        
	      }
	      	//going to next order equations
	      	currentOrder += 1	
	      }
	            
	    } 
	  
	  def assign(name:String,value:String)
	  {
	    changeCustomAttr(name,value)
	  }
	  
	  def getCustomAttByName(ca:String):CustomAttribute = 
	  {
	    var a:CustomAttribute =null
	    var l = List((ca,1.0))
	    getVirtualFromTerm(l)
	    
	    if(CA.contains(ca))
	    {
	      a = CA(ca)._1	      
	    }
	    
	    a
	  }

}
}